"""随机森林回归（Random Forest Regression）是一种在机器学习领域广泛应用的算法，由美国科学家 Leo Breiman 在2001年提出。
它是一种集成学习方法，通过整合多个决策树的预测结果来提高预测精度和稳定性。

随机森林回归适用于各种需要预测连续数值输出的问题，
如金融领域的股票价格预测、客户信用评分，医疗领域的疾病诊断和药物发现等。

1. 算法概述
随机森林回归算法通过引入随机性来构建多个决策树，再通过对这些树的预测结果进行平均或投票来得出最终的预测结果。
这里的随机性主要体现在两个方面：一是训练样本的随机选取，二是在训练过程中特征的随机选取。

随机森林的算法过程并不复杂，主要的步骤如下：

从原始训练集中随机选择一部分样本，构成一个新的子样本集。这样可以使得每棵决策树都在不同的样本集上进行训练，增加模型的多样性。
对于每个决策树的每个节点，在选择最佳划分特征时，只考虑随机选择的一部分特征。这样可以防止某些特征对整个模型的影响过大，提高模型的鲁棒性。
在每个子样本集上使用某种决策树算法构建一棵决策树。决策树的生长过程中，通常采用递归地选择最佳划分特征，将数据集划分为不纯度最小的子集。
通过上述步骤生成的大量决策树最终组合成随机森林。
上面第一，第二步骤中的随机性就是随机森林这个名称的由来。

2. 创建样本数据
这次的回归样本数据，我们用 scikit-learn 自带的样本生成器来生成回归样本。
关于样本生成器的内容，可以参考：TODO
"""
from sklearn.datasets import make_regression
from sklearn.tree import DecisionTreeRegressor
from sklearn import metrics
from sklearn import preprocessing as pp
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor


# 回归样本生成器
X, y = make_regression(n_features=4, n_informative=2)
"""每个样本有4个特征。

3. 模型训练
训练之前，为了减少算法误差，先对数据进行标准化处理。"""


# 数据标准化
X = pp.scale(X)
y = pp.scale(y)
# 接下来分割训练集和测试集。


# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)
# 然后用scikit-learn中的RandomForestRegressor模型来训练：


# 定义随机森林回归模型
reg = RandomForestRegressor(max_depth=2)

# 训练模型
reg.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = reg.predict(X_test)
"""RandomForestRegressor的主要参数包括：

n_estimators：森林中决策树的数量。默认值为100，表示这是森林中树木的数量，即基评估器的数量。但是，任何模型都有决策边界，
当n_estimators达到一定的程度之后，随机森林的精确性往往不再上升或开始波动。
同时，n_estimators越大，需要的计算量和内存也越大，训练的时间也会越来越长。
max_depth：树的最大深度。默认是None，与剪枝相关。设置为None时，
树的节点会一直分裂，直到每个叶子都是“纯”的，或者叶子中包含于min_samples_split个样本。可以从3开始尝试增加，观察是否应该继续加大深度。
min_samples_split：在叶节点处需要的最小样本数。默认值是2，指定每个内部节点（非叶子节点）包含的最少的样本数。
min_samples_leaf：每个叶子结点包含的最少的样本数。参数的取值除了整数之外，还可以是浮点数。如果参数的值设置过小会导致过拟合，反之就会欠拟合。
min_weight_fraction_leaf：叶子节点所需要的最小权值。
max_features：用于限制分枝时考虑的特征个数。超过限制个数的特征都会被舍弃。此参数可以设为整数、浮点数、字符或None，默认为'auto'。
max_leaf_nodes：最大叶子节点数，整数，默认为None。这个参数通过限制树的最大叶子数量来防止过拟合，
如果设置了一个正整数，则会在建立的最大叶节点内的树中选择最优的决策树。
min_impurity_decrease：如果分裂指标的减少量大于该值，则进行分裂。
min_impurity_split：决策树生长的最小纯净度。默认是0。
最后验证模型的训练效果："""

# 在测试集上进行预测
y_pred = reg.predict(X_test)

mse, r2, m_error = 0.0, 0.0, 0.0
y_pred = reg.predict(X_test)
mse = metrics.mean_squared_error(y_test, y_pred)
r2 = metrics.r2_score(y_test, y_pred)
m_error = metrics.median_absolute_error(y_test, y_pred)

print("均方误差：{}".format(mse))
print("复相关系数：{}".format(r2))
print("中位数绝对误差：{}".format(m_error))

"""# 运行结果
均方误差：0.0918182629293023
复相关系数：0.9137032593574914
中位数绝对误差：0.17199566634564867
从预测的误差来看，训练的效果非常好。

有同样的数据试了下上一篇介绍的决策树回归算法，发现还是随机森林回归的效果要好一些。
决策数回归的模型效果："""

# 定义决策树回归模型
reg = DecisionTreeRegressor(max_depth=2)

# 训练模型
reg.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = reg.predict(X_test)


mse, r2, m_error = 0.0, 0.0, 0.0
y_pred = reg.predict(X_test)
mse = metrics.mean_squared_error(y_test, y_pred)
r2 = metrics.r2_score(y_test, y_pred)
m_error = metrics.median_absolute_error(y_test, y_pred)

print("均方误差：{}".format(mse))
print("复相关系数：{}".format(r2))
print("中位数绝对误差：{}".format(m_error))

"""# 运行结果
均方误差：0.1681399575883647
复相关系数：0.8419711956126009
中位数绝对误差：0.36483491370039456
从运行结果来看，决策树回归的误差比随机森林回归要大不少。

4. 总结
随机森林回归算法的优势主要在于可以有效地处理大量的输入变量，并且可以处理非线性关系和交互作用，
同时 ，由于它是集成学习方法，所以可以有效地减少过拟合和欠拟合的问题，提高预测的准确性。

此外，在训练过程中，它可以自动进行特征选择和降维，帮助找到最重要的特征，
还可以处理缺失值和异常值，不需要进行特殊的数据预处理。

然而，随机森林回归算法也有一些劣势，
首先，它的训练过程相对较慢，尤其是在数据集较大或特征维度较高的情况下；
其次，在某些情况下，它可能过于依赖输入数据的随机性，导致预测结果的不稳定。
此外，随机森林算法在处理那些需要精确控制的问题时可能效果不佳。"""